﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Runtime.InteropServices;
using System.Runtime.CompilerServices;
using System.Threading;

using agree.schema;

using alib;
using alib.Array;
using alib.Concurrency;
using alib.Enumerable;
using alib.Collections;
using alib.Collections.ReadOnly;
using alib.Debugging;

namespace agree
{
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// 
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	public abstract class _tfs_resources : identity_seq, IRtChild
	{
		protected _tfs_resources(IRtParent parent, IIdentity prv, Restrictor r)
			: base(prv)
		{
			this.parent = parent;
			this.restrictor = r;
		}

		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		protected readonly IRtParent parent;
		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		public IRtParent Parent { get { return parent; } }

		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		public readonly Restrictor restrictor;

		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		public bool AnyRestriction { get { return restrictor != null && restrictor.RestrictedCount > 0; } }


		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		protected Grammar ____g;
		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		public Grammar Grammar { [DebuggerStepThrough]  get { return ____g ?? restrictor.FeatMgr.Grammar; } }
		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		public FeatMgr ftm { [DebuggerStepThrough] get { return Grammar.FeatMgr; } }
		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		public TfsUnifier fsu { [DebuggerStepThrough] get { return Grammar.fsu; } }
		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		public TypeUtils tu { [DebuggerStepThrough] get { return Grammar.tu; } }
		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		public TypeDictionary td { [DebuggerStepThrough] get { return Grammar.td; } }
		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		public MrsManager mrsm { [DebuggerStepThrough] get { return Grammar.mrsm; } }
		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		public Isometrics im { [DebuggerStepThrough] get { return Grammar.im; } }
		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		public EntryMgr em { [DebuggerStepThrough] get { return Grammar.em; } }
		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		public NodeLabeler nl { [DebuggerStepThrough] get { return Grammar.nl; } }
		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		public semi.Vpm vpm { [DebuggerStepThrough] get { return Grammar.vpm; } }
	};


	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// <summary>
	/// base class for 'Typed Feature Structure'
	/// </summary>
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	[DebuggerDisplay("{ToString(),nq}")]
#if DEBUG
	[DebuggerTypeProxy(typeof(arraytfs_debug_display))]
#endif
	public abstract partial class Tfs : _tfs_resources, ITfsSlot, ρρRenderNode, ρρType
	{
		public const int TopmostMark = 1;

		protected Tfs(IRtParent parent, IIdentity prv, Restrictor r, Tfs.Flags flags)
			: base(parent, prv, r)
		{
			this.flags = flags;
		}
		protected Tfs(IRtParent parent, Tfs to_clone)
			: this(parent, to_clone, to_clone.restrictor, to_clone.flags)
		{
			this._top_edge = to_clone._top_edge;
			this.c_act = to_clone.c_act;
			this.entries = to_clone.entries;
			this.c_corefs = to_clone.c_corefs;
			this.trigger_rule_regexes = to_clone.trigger_rule_regexes;
		}

		public Edge _top_edge;

		public Type Type
		{
			get { return tu.GetEdgeType(_top_edge.FlagsId); }
			set { throw not.impl; }
		}

		[DebuggerDisplay("{this.SlotRefArray}")]
		public arr_tfs_entry[] entries;

		public int c_act;

		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		public int c_corefs;

		public Flags flags;

		[Flags]
		public enum Flags : int
		{
			None /*			*/ = 0,
			Definition /*	*/ = 0x00000001,
			Expanded /*		*/ = 0x00000002,
			RootCoverage /*	*/ = 0x00000004,
			MultiRoot /*	*/ = 0x00000008,
			DeleteArgs /*	*/ = 0x00000010,
			Restrict /*		*/ = 0x00000020,
			Skolemized /*	*/ = 0x00000040,
			WantVarbits /*	*/ = 0x00000080,
			FromVpm /*      */ = 0x02000000,
		};

		public bool SemanticsRestricted
		{
			[DebuggerStepThrough]
			get { return (flags & Flags.Restrict) != 0; }
		}

		public bool ArgsRestricted
		{
			[DebuggerStepThrough]
			get { return (flags & Flags.DeleteArgs) != 0; }
		}

		public String FlagsText
		{
			get
			{
				String s = "";
				if (flags.HasFlag(Flags.Definition))
					s += "def ";
				if (flags.HasFlag(Flags.Expanded))
					s += "exp ";
				if (flags.HasFlag(Flags.RootCoverage))
					s += "root_cov ";
				if (flags.HasFlag(Flags.MultiRoot))
					s += "multi_root ";
				if (flags.HasFlag(Flags.DeleteArgs))
					s += "del_dtrs ";
				if (flags.HasFlag(Flags.Restrict))
					s += "restrict ";
				if (flags.HasFlag(Flags.Skolemized))
					s += "skol ";
				if (flags.HasFlag(Flags.WantVarbits))
					s += "varbits ";
				if (flags.HasFlag(Flags.FromVpm))
					s += "vpm ";
				return s.TrimEnd();
			}
		}

		[DebuggerStepThrough]
		protected unsafe void _set_flag(Flags f)
		{
			Flags cur, prv = flags;
			if ((prv & f) != f)
				fixed (Flags* pf = &flags)
					do
						prv = (Flags)Interlocked.CompareExchange(ref *(int*)pf, (int)(cur = prv) | (int)f, (int)prv);
					while (prv != cur && (prv & f) != f);
		}
		[DebuggerStepThrough]
		protected unsafe void _clear_flag(Flags f)
		{
			Flags cur, prv = flags;
			if ((prv & f) != 0)
				fixed (Flags* pf = &flags)
					do
						prv = (Flags)Interlocked.CompareExchange(ref *(int*)pf, (int)(cur = prv) & ~(int)f, (int)prv);
					while (prv != cur && (prv & f) != 0);
		}

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		///
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		public TfsSlot MrsSlot
		{
			get { return this[mrsm.InitialSemanticsPath]; }
		}

		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		agree.mrs.Mrs _mrs;
		public agree.mrs.Mrs Mrs(semi.Vpm vpm = null)
		{
			var _tmp = _mrs;
			return _tmp ?? (_mrs = agree.mrs.Mrs.FromTfs(this, vpm));
		}

		/// <summary> lightweight PRED and CARG extraction </summary>
		public unsafe LexLookupKey[] _get_rel_keys(out int ix_out)
		{
			if (mrsm == null)
				goto no_semantics;

			LexLookupKey* p_base = stackalloc LexLookupKey[100];
			ix_out = -1;
			int mx, i = 0;
			for (int m = this[mrsm.FullRelsPath].out_mark; m != 0 && this.TryGetFlagsMark(im.f_ix_list_head, m, out mx) != 0; i++)
			{
				p_base[i] = new LexLookupKey(this, mx);

				if (ix_out == -1 || p_base[i].carg != 0)
					ix_out = i;

				if (this.TryGetFlagsMark(im.f_ix_list_tail, m, out m) == 0 || m == 0)
					goto no_semantics;
			}
			int c = i;
			if (c > 0)
			{
				Debug.Assert(p_base[ix_out].IsValid);
				LexLookupKey[] _tmp = new LexLookupKey[c];
				for (i = 0; i < c; i++)
					_tmp[i] = p_base[i];

				return _tmp;
			}
		no_semantics:
			ix_out = -1;
			return Collection<LexLookupKey>.None;
		}

		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		public Edge.Flag[] RelPreds
		{
			get
			{
				int c;
				var rk = _get_rel_keys(out c);
				if ((c = rk.Length) == 0)
					return Collection<Edge.Flag>.None;
				var arr = new Edge.Flag[c];
				for (int i = 0; i < c; i++)
					arr[i] = rk[i].pred;
				return arr;
			}
		}

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		///
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		Tfs ITfsSlot.Tfs { [DebuggerStepThrough] get { return this; } }
		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		int ITfsSlot.SlotIndex { [DebuggerStepThrough] get { return 0; } }

		public abstract bool TryGetEdge(int i_feat, int mark, out Edge e);
		public abstract Edge.Flag TryGetFlagsMark(int i_feat, int mark, out int m);
		public unsafe abstract int GetEdgeIndex(int i_feat, int mark, Edge* pe);
		public unsafe abstract void GetIndexData(int ix1, arr_tfs_entry* pate);
		public abstract int EdgeCount { get; }
		public virtual int SlotCount { get { return EdgeCount; } }

		[MethodImpl(MethodImplOptions.AggressiveInlining), DebuggerStepThrough]
		public unsafe int GetEdgeIndex(int i_feat, int mark) { return GetEdgeIndex(i_feat, mark, null); }

#if REVERSE_INDEX
		public virtual Edge GetCorefEdge(int out_mark) { throw new InvalidOperationException(); }
#endif

		public TfsSlot this[FeatureInfo fi] { [DebuggerStepThrough] get { return this[fi.i_feat]; } }

		public TfsSlot this[String s_path] { [DebuggerStepThrough] get { return _ts_ext.Next(this, s_path); } }

		public TfsSlot this[FsPath fsp] { [DebuggerStepThrough] get { return fsp[new TfsSlot(this, 0)]; } }

		public unsafe TfsSlot this[int i_feat]
		{
			[DebuggerStepThrough]
			get
			{
				int ix1;
				if (i_feat < 0 || (ix1 = GetEdgeIndex(i_feat, _top_edge.Mark, null)) < 0)
					return TfsSlot.NotValid;
				if (ix1 == 0)
					throw new Exception();
				return new TfsSlot(this, ix1);
			}
		}

		[MethodImpl(MethodImplOptions.AggressiveInlining), DebuggerStepThrough]
		public unsafe Edge GetIx1Edge(int ix1)
		{
			if (ix1 == 0)
				return _top_edge;
			arr_tfs_entry ate;
			GetIndexData(ix1, &ate);
			return ate.e;
		}
		[MethodImpl(MethodImplOptions.AggressiveInlining), DebuggerStepThrough]
		public int GetIx1Mark(int ix1) { return GetIx1Edge(ix1).Mark; }

		public unsafe int GetIx1ForListPosition(int ix1, int i_pos)
		{
			arr_tfs_entry ate;
			int ix1_head;
			do
			{
				GetIndexData(ix1, &ate);

				if (ate.e_Mark == 0 || (ix1_head = GetEdgeIndex(im.f_ix_list_head, ate.e_Mark)) <= 0)
					break;

				if (i_pos == 0)
					return ix1_head;

				i_pos--;
			}
			while ((ix1 = GetEdgeIndex(im.f_ix_list_tail, ate.e_Mark)) > 0);
			return -1;
		}

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		///
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

		[DebuggerDisplay("mark:{mark}")]
		public sealed class qc_cache
		{
			public qc_cache(int mark, Edge.Flag[] rgqc)
			{
				this.mark = mark;
				this.rgqc = rgqc;
			}
			[DebuggerBrowsable(DebuggerBrowsableState.Never)]
			public int mark;
			[DebuggerBrowsable(DebuggerBrowsableState.Never)]
			public qc_cache next;
			[DebuggerBrowsable(DebuggerBrowsableState.RootHidden)]
			public Edge.Flag[] rgqc;
		}

		qc_cache qcc;
		public static Edge.Flag[] QcPaths(QuickCheck qc, Tfs tfs, int mark)
		{
			qc_cache _new = null, _cmp, _cur = tfs.qcc;
			do
			{
				while (_cur != null)
				{
					if (_cur.mark == mark)
						return _cur.rgqc;
					_cur = _cur.next;
				}

				if (_new == null)
				{
#if QC_CIL
					var rgqc = qc.QcWalk(tfs, mark);
#else
					var rgp = tm.g.qc;
					var rgqc = new Edge.Flag[rgp.Length];
					for (int i = 0; i < rgp.Length; i++)
						rgqc[i] = rgp[i].GetFlag(this, mark) & ~Edge.Flag.Coreference;
#endif
					_new = new qc_cache(mark, rgqc);
				}
#if DEBUG
				Nop.CodeCoverage(_cur != null, "using multiple QC caches");
#endif
				_new.next = _cur;
			}
			while ((_cur = Interlocked.CompareExchange(ref tfs.qcc, _new, _cmp = _cur)) != _cmp);
			return _new.rgqc;
		}

		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		qc_cache[] _dbg_qc_cached { get { return _qc_cache.ToArray(); } }
		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		IEnumerable<qc_cache> _qc_cache
		{
			get
			{
				qc_cache _cur = this.qcc;
				while (_cur != null)
				{
					yield return _cur;
					_cur = _cur.next;
				}
			}
		}

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		///
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

		//map from slot index (int) to pred regex
		public Dictionary<int, System.Text.RegularExpressions.Regex> trigger_rule_regexes;

		public unsafe transfer.old.TaggedRelMode GetIx1RmGgix(int ix1)
		{
			arr_tfs_entry ate;
			GetIndexData(ix1, &ate);
			return ate.tgrm;
		}

		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		public IRenderNode RenderNode
		{
			get { return new RootRenderNode(this); }
			set { throw not.impl; }
		}

		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		public abstract SlotRefArray SlotRefArray { get; }

		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		public ztfs Ztfs { [DebuggerStepThrough] get { return new ztfs(this); } }
	};
}
